欢迎Follow我的Github,博客会同步在Github的Blog仓库更新。
- Github地址: LeoMobileDeveloper
前言
像C++,Objective C都是编译语言。编译语言在执行的时候,必须先通过编译器生成机器码,机器码可以直接在CPU上执行,所以执行效率较高。
像JavaScript,Python都是直译式语言。直译式语言不需要经过编译的过程,而是在执行的时候通过一个中间的解释器将代码解释为CPU可以执行的代码。所以,较编译语言来说,直译式语言效率低一些,但是编写的更灵活,也就是为啥JS大法好。
iOS开发目前的常用语言是:Objective和Swift。二者都是编译语言,换句话说都是需要编译才能执行的。二者的编译都是依赖于Clang(swift) + LLVM. 篇幅限制,本文只关注Objective C,因为原理上大同小异。
可能会有同学想问,我不懂编译的过程,写代码也没问题啊?这点我是不否定的。但是,充分理解了编译的过程,会对你的开发大有帮助。本文的最后,会以以下几个例子,来讲解如何合理利用XCode和编译
__attribute__
- Clang警告处理
- 预处理
- 插入编译期脚本
- 提高项目编译速度
对于不想看我啰里八嗦讲一大堆原理的同学,可以直接跳到本文的最后一个章节。
iOS编译
Objective C采用Clang作为前端,而Swift则采用swift()作为前端,二者LLVM(Low level vritual machine)作为编译器后端。所以简单的编译过程如图
其中,swift的编译命令可以在这里找到
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift
可以通过Clang,来查看一个文件的编译具体过程,新建Demo.m
#import <Foundation/Foundation.h>
int main(){
@autoreleasepool {
NSLog(@"%@",@"Hello Leo");
}
return 0;
}
然后终端输入:
clang -ccc-print-phases -framework Foundation Demo.m -o Demo
0: input, "Foundation", object
1: input, "Demo.m", objective-c
2: preprocessor, {1}, objective-c-cpp-output//预处理
3: compiler, {2}, ir //编译生成IR(中间代码)
4: backend, {3}, assembler//汇编器生成汇编代码
5: assembler, {4}, object//生成机器码
6: linker, {0, 5}, image//链接
7: bind-arch, "x86_64", {6}, image//生成Image,也就是最后的可执行文件
接着,就可以在终端直接运行这个程序了:
./Demo
Leo$ ./Demo
Demo[923:24816] Hello Leo
编译器前端
编译器前端的任务是进行:语法分析,语义分析,生成中间代码(intermediate representation )。在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。
编译器后端
编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化。iOS的编译过程,后端的处理如下
- LVVM优化器会进行BitCode的生成,链接期优化等等。
- LLVM机器码生成器会针对不同的架构,比如arm64等生成不同的机器码。
执行一次XCode build的流程
当你在XCode中,选择build的时候(快捷键command+B),会执行如下过程
- 编译信息写入辅助文件,创建编译后的文件架构(name.app)
- 处理文件打包信息,例如在debug环境下
Entitlements:
{
"application-identifier" = "app的bundleid";
"aps-environment" = development;
}
- 执行CocoaPod编译前脚本
- 例如对于使用CocoaPod的工程会执行
CheckPods Manifest.lock
- 例如对于使用CocoaPod的工程会执行
- 编译各个.m文件,使用
CompileC
和clang
命令。
CompileC ClassName.o ClassName.m normal x